#pragma once

#include <QList>
#include <QString>
#include <QStringList>
#include "atomset.h"
#include "residue.h"

namespace PBVP {
	class ProteinData;

	///
	class ProteinMetaData {
	public:
		QString classification;
		QString date;
		QString ID;
	};

	/// One chain of protein complex
	class ChainData : public AtomSet {
	public:
		ChainData(char id, ProteinData *pProtein);
		///
		~ChainData() {}
		///
		virtual QVector<Atom> getAtoms();

		char id() const {
			return m_Id;
		}

		///
		void print() const;
		/// Add residue to most recent chain
		/** 
		* Because PDB file does not have atoms available for every 
		* residue. Residues should be added when parsing ATOMS instead 
		* of SEQRES 
		* 
		*/
		void addResidue(const QString& resName);

		void addAtom(const Atom& atom);

		void setChainOffset(int n) {
			m_ChainOffset = n;
		}

		int numOfResidues() const {
			return m_Residues.size();
		}
		int numOfAtoms() const {
			return m_Atoms.size();
		}
		/// Get a sequence of backbone atoms
		QList<Atom> getBackboneAtoms() const;

	protected:
		char m_Id;
		
		ProteinData *m_pProtein;

		QVector<int> m_Residues;
		QVector<Atom> m_Atoms;

		int m_ChainOffset;
	};
	
	///
	class LigandData : public AtomSet {
	public:
		LigandData(const QString &_name, ProteinData *pProtein);
	
		virtual QVector<Atom> getAtoms(); 
		
		const QString& name() const {
			return m_Name;
		}
		///
		void print() const;

		/// 
		void addAtom(const Atom& atom);
		///
		int numOfAtoms() const {
			return m_Atoms.size();
		}
	protected:
		QString m_Name;

		ProteinData *m_pProtein;

		QVector<Atom> m_Atoms;
	};

	/// 
	class ProteinData : public AtomSet {
	public:
		ProteinData();
		///
		virtual ~ProteinData();

		virtual DataSetType getDataSetType() const {
			return PROTEIN_DATA;
		}

		virtual QVector<Atom> getAtoms();

		///
		const ProteinMetaData& getMetaData() const {
			return m_MetaData;
		}
		///
		void setMetaData(const ProteinMetaData& meta);
		/// Add a chain with name "id" 
		/**
		 * @return The pointer to the newly added chain
		 */
		ChainData* addChain(char id);
		/// Get the chain with name "id"
		ChainData* getChain(char id);
		/// Get the ith chain
		ChainData* getChain(int i);

		///
		LigandData* addLigand(const QString& name);
		///
		LigandData* getLigand(const QString& name);
		/// 
		LigandData* getLigand(int i);
		///
		int numOfAtoms() const {
			int n = 0;
			for(int i = 0; i < numOfChains(); i++) {
				n += m_ChainList[i]->numOfAtoms();
			}
			return n;
		}
		///
		int numOfChains() const {
			return m_ChainList.size();
		}
		///
		int numOfResidues() const {
			int n = 0;
			for(int i = 0; i < numOfChains(); i++) {
				n += m_ChainList[i]->numOfResidues();
			}
			return n;
		}
		/// 
		int numOfLigands() const {
			return m_LigandList.size();
		}

		QStringList getChainNames() const;

		QStringList getLigandNames() const;

		/// Print out information contained in the protein
		void print() const;
		
	protected:
		friend class ChainData;
		friend class LigandData;

		/// list of chains of the protein
		QList<ChainData *> m_ChainList;

		///
		QList<LigandData *> m_LigandList;

		ProteinMetaData m_MetaData;
	};
}